#!/usr/bin/env python
#-*- coding: utf-8 -*-

import sys
import gnome.ui
import gnomeapplet
import gtk
import pango
import cairo
import math

import uspCoreUtils 

import utils.GConfigurator as uspGConfig
import utils.OptionDialog as uspOptionDialog
import utils.ButtonFactory as uspButtonFactory
import utils.IconFactory as uspIconFactory

class PluginConfiguration(object):
	def __init__(self, mainWindow):
		self.mainWindow = mainWindow
	
	def hide(self):
		self.mainWindow.set_property('visible', False)
		
class PluginHolder(gtk.EventBox):
	dragAndDropTargets = [("STRING", gtk.TARGET_SAME_APP , 0)]
	
	def __init__(self, plugin, mainWindow):
		gtk.EventBox.__init__(self)
		self.mainWindow = mainWindow
		
		self.connect("drag_data_received", self.onDragDataRecieved)
		self.drag_dest_set(gtk.DEST_DEFAULT_ALL, self.dragAndDropTargets, gtk.gdk.ACTION_COPY | gtk.gdk.ACTION_MOVE)
		
		self.connect("drag_data_get", self.onDragDataSend)
		self.drag_source_set(gtk.gdk.BUTTON1_MASK, self.dragAndDropTargets, gtk.gdk.ACTION_COPY | gtk.gdk.ACTION_MOVE)
		
		self.connect("drag_begin", self.onDragBegin)
		
		pluginWidget = PluginWidget(plugin, mainWindow)
		self.add(pluginWidget)
		
	def onDragDataRecieved(self, widget, dragContext, x, y, selection, targetType, time):
		if targetType == 0:
			sourceWidget = dragContext.get_source_widget()
			if sourceWidget == widget:
				return
			
			child = widget.get_child()
			sourceChild = sourceWidget.get_child()
			
			widget.remove(child)
			sourceWidget.remove(sourceChild)
			
			widget.add(sourceChild)
			sourceWidget.add(child)
			
			self.mainWindow.resize(1, 1)
			
	def onDragDataSend(self, widget, dragContext, selection, targetType, time):
		selection.set_text("Button")
	
	def onDragBegin(self, widget, dragContext):
		pb = gtk.gdk.Pixbuf.get_from_drawable(
			gtk.gdk.Pixbuf(
				gtk.gdk.COLORSPACE_RGB, 
				True, 
				8, 
				widget.allocation.width,
				widget.allocation.height),
			widget.window,
			widget.get_colormap(),
			0, 0, 0, 0, 
			widget.allocation.width,
			widget.allocation.height)
		x, y, mask = widget.window.get_pointer()
		dragContext.set_icon_pixbuf(pb,x,y)
		

class PluginWidget(gtk.VBox):
	def __init__(self, plugin, mainWindow):
		gtk.VBox.__init__(self)
		
		self.plugin = plugin
		self.mainWindow = mainWindow
		
		self.set_border_width(2)
		
		buttonFactory = uspButtonFactory.ButtonFactory()
		buttonFactory.iconSize = 24
		buttonFactory.minLabelWidth = 25
		buttonFactory.labelAlignment = 0.5
		buttonFactory.addEvent("button_press_event", self.onButtonPressEvent)
		
		hBox = gtk.HBox()
		self.buttonIcon = buttonFactory.create([], plugin.pluginIcon())
		
		hBox.pack_start(self.buttonIcon, False, False, 0)
		self.label = gtk.Label(plugin.pluginName())
		self.label.set_single_line_mode(True)
		self.label.set_width_chars(25)
		self.label.set_ellipsize(pango.ELLIPSIZE_END)
		itemStyle = pango.AttrList()
		itemStyle.insert(pango.AttrSize(13 * 1000, 0, -1))
		self.label.set_attributes(itemStyle)
		self.label.set_alignment(0.5, 0.5)
			
		hBox.pack_start(self.label, True, True, 0)
		self.pack_start(hBox, False, False, 0)
		
		self.plugin.mainWidget().unparent()
		
		self.plugin.configuration = PluginConfiguration(mainWindow)
		
		self.pack_start(self.plugin.mainWidget(), True, True, 0)
		
		if mainWindow.isCompositing:
			plugin.mainWidget().connect('expose-event', uspCoreUtils.TransparencyUtils.clearWithColorExposeFunction, self.mainWindow.bgColor)
			uspCoreUtils.TransparencyUtils.setColormapToScreenColormap(plugin.mainWidget())
			
		if hasattr(plugin, 'getPluginSize'):
			width, height = plugin.getPluginSize()
			self.plugin.mainWidget().set_size_request(width, height)
			self.mainWindow.resize(1, 1)
	
	def showHide(self, widget):
		visible = widget.get_property('visible')
		widget.set_property('visible', not visible)
	
	def onButtonPressEvent(self, widget, event):
		if event.type == gtk.gdk.BUTTON_PRESS:
			if event.button == 1:
				self.onButton1(widget, event)
			elif event.button == 3:
				self.onButton3(widget, event)
			else:
				return
			self.update()
	
	def update(self):
		mainWindow.resize(1,1)
		
	def onButton1(self, widget, event):
		self.showHide(self.plugin.mainWidget())
		pass
	
	def onButton3(self, widget, event):
		if not hasattr(self.plugin, 'getPluginSize'):
			return
		width, height = self.plugin.getPluginSize()
		
		#if hasattr(self.plugin, 'getOptions'):
		#	pluginOptions = self.plugin.getOptions()
			
		dialog = uspOptionDialog.OptionDialog("Plugin Properties", self.mainWindow)
		
		options = [
			uspOptionDialog.IntegerOption('Width', width, upper = 1000), 
			uspOptionDialog.IntegerOption('Height', height, upper = 1000)
		]
		#options.extend(pluginOptions)
		options = dialog.run(options)
		if options == None:
			return
		width, height = options
		
		#if hasattr(self.plugin, 'setOptions'):
			#self.plugin.setOptions(options)
			
		self.plugin.mainWidget().set_size_request(width, height)
		if hasattr(self.plugin, 'setPluginSize'):
			self.plugin.setPluginSize(width, height)

class PluginGroup(gtk.VBox):
	dragAndDropTargets = [("STRING", gtk.TARGET_SAME_APP , 0)]
	
	def __init__(self, pluginNames, allPluginsByName, mainWindow):
		gtk.VBox.__init__(self)
		
		self.pluginsByName = allPluginsByName 
		self.mainWindow = mainWindow
		
		self.set_border_width(5)
		
		self.connect("drag_data_received", self.onDragDataRecieved)
		self.drag_dest_set(gtk.DEST_DEFAULT_ALL, self.dragAndDropTargets, gtk.gdk.ACTION_COPY | gtk.gdk.ACTION_MOVE)
		
		self.initPlugins(pluginNames)
		
	def initPlugins(self, pluginNames):
		for pluginName in pluginNames:
			plugin = self.pluginsByName[pluginName]
			pluginHolder = PluginHolder(plugin, self.mainWindow)
			self.mainWindow.registeredPlugins.append(plugin);
			self.pack_start(pluginHolder, False, False, 0)
		
	def onDragDataRecieved(self, targetBox, dragContext, x, y, selection, targetType, time):
		if targetType == 0:
			sourceWidget = dragContext.get_source_widget()
			sourceBox = sourceWidget.get_parent()
			if targetBox == sourceBox:
				return
			sourceBox.remove(sourceWidget)
			targetBox.pack_start(sourceWidget, False, False, 0)
			
class MainConfiguration(object):
	pass
	
class MainWindow(gtk.Window):
	def __init__(self):
		gtk.Window.__init__(self, gtk.WINDOW_TOPLEVEL)
		
		self.gClient = uspGConfig.GConfigurator('/apps/usp2')
		self.configure()
		
		# Window properties
		self.set_resizable(False)
		self.set_modal(False)
		self.set_keep_above(True)
		self.set_double_buffered(True)
		self.set_decorated(False)
		self.set_deletable(False)
		self.set_type_hint(gtk.gdk.WINDOW_TYPE_HINT_DIALOG)
		
		# Window signal handlers
		self.connect("show", self.onShow)
		
		if self.configuration.roundBorders:
			self.connect('size-allocate', self.onSizeAllocate)
		
		self.isCompositing = self.is_composited() and self.configuration.opacity > 0
		
		print self.isCompositing
		if self.isCompositing:
			uspCoreUtils.TransparencyUtils.setColormapToScreenColormap(self)
		
		# Window realization
		self.realize()
		
		style = self.get_style()
		color = style.bg[gtk.STATE_NORMAL]
		
		red = color.red / 65536.0
		green = color.green / 65536.0
		blue = color.blue / 65536.0
		alpha = self.configuration.opacity/100.0
		
		self.bgColor = (red, green, blue, alpha)
		
		if self.isCompositing:
			self.connect('expose-event', uspCoreUtils.TransparencyUtils.clearWithColorExposeFunction, self.bgColor)
		
		self.registeredPlugins = []
		
		self.reload()
	
	def configure(self):
		self.configuration = MainConfiguration()
		self.configuration.roundBorders = self.gClient.getBoolean('roundBorders', False)
		self.configuration.opacity = self.gClient.getInteger('opacity', -1)
	
	def onShow(self, widget):
		self.entry.set_text("")
		
	def onSizeAllocate(self, widget, allocation):
		w,h = allocation.width, allocation.height
		padding=5
		rounded=20
		
		pixmap = gtk.gdk.Pixmap(None, w, h, 1)
		cr = pixmap.cairo_create()
		cr.set_source_rgba(0.0, 0.0, 0.0, 1.0)
		cr.set_operator(cairo.OPERATOR_DEST_OUT)
		cr.paint()
		
		cr.set_operator(cairo.OPERATOR_OVER)
		cr.set_source_rgba(0.0, 0.0, 0.0, 1.0)
		cr.move_to(0+padding+rounded, 0+padding)
		cr.line_to(w-padding-rounded, 0+padding)
		cr.arc(w-padding-rounded, 0+padding+rounded, rounded, math.pi/2, 0)
		cr.line_to(w-padding, h-padding-rounded)
		cr.arc(w-padding-rounded, h-padding-rounded, rounded, 0, math.pi/2)
		cr.line_to(0+padding+rounded, h-padding)
		cr.arc(0+padding+rounded, h-padding-rounded, rounded, math.pi+math.pi/2, math.pi)
		cr.line_to(0+padding, 0+padding+rounded)
		cr.arc(0+padding+rounded, 0+padding+rounded, rounded, math.pi/2, 0)
		cr.fill()
		
		self.shape_combine_mask(pixmap, 0, 0)
		
	def reload(self):
		self.pluginsByName = self.initPlugins()
		self.initView()
		
	def grabFocus(self):
		self.grab_focus()
		self.entry.grab_focus()
		self.present()
		self.stick()
		
	def initPlugins(self):
		# move to core utils... Importer should handle this
		plugins = uspCoreUtils.Importer().plugins()
		pluginsByName = {}
		for plugin in plugins:
			if not hasattr(plugin, 'UspPlugin'):
				continue
			pluginInstance = plugin.UspPlugin()
			pluginName = pluginInstance.pluginName()
			print "Loaded: ", plugin.__file__
			pluginsByName[pluginName] = pluginInstance
		return pluginsByName
		
	def initView(self):
		self.border = gtk.Alignment(0.5, 0.5, 1.0, 1.0)
		self.border.set_padding(15, 15, 15, 15)
		self.add(self.border)
		
		self.hbox = gtk.HBox()
		self.border.add(self.hbox)
		
		self.vbox = gtk.VBox()
		self.hbox.pack_start(self.vbox, True, True, 0)
		
		self.pluginsBox = self.addPlugins()
		self.vbox.pack_start(self.pluginsBox, True, True, 0)
		
		self.entry = gtk.Entry()
		self.entry.connect("changed", self.onEntryChanged) 
		self.entry.connect("activate", self.onEntryActivated)
		self.vbox.pack_start(self.entry, False, False, 0)
		
		self.border.show_all()
		
	def onEntryChanged(self, widget):
		self.updatePlugins(widget.get_text())
		
	def onEntryActivated(self, widget):
		text = widget.get_text()
		#todo: parse input
	
	def updatePlugins(self, text):
		for plugin in self.registeredPlugins:
			if hasattr(plugin, 'filterEntryChanged'):
				plugin.filterEntryChanged(text)
	
	def addPlugins(self):
		plugins = [['User', 'System info', 'System management'],
			['Applications','Favourites'],
			['Recent', 'Search']]
		
		hbox = gtk.HBox()
		
		for verticalPluginNames in plugins:
			group = PluginGroup(verticalPluginNames, self.pluginsByName, self)
			hbox.pack_start(group, True, True, 0)
			
		return hbox

class AppletWidget(gtk.ToggleButton):
	def __init__(self, applet):
		gtk.ToggleButton.__init__(self)
		self.applet = applet
		self.set_relief(gtk.RELIEF_NONE)
		self.set_label("USP2")
		self.connect("button-press-event", self.onButtonPressEvent)
		
		self.connect('drag-motion', self.onDragMotion)
		self.connect("drag-leave", self.onDragLeave)
		
		self.drag_dest_set(0, [], 0)
		self.dragging = False
		
		self.set_image(gtk.image_new_from_icon_name("distributor-logo", gtk.ICON_SIZE_BUTTON))
		
		self.mainWindow = MainWindow()
		self.mainWindow.add_events(gtk.gdk.FOCUS_CHANGE)
		self.mainWindow.connect("focus_out_event", self.onFocusChange)
		
		import deskbar.core.keybinder as keybinder
		keybinder.tomboy_keybinder_bind("<ctrl>F12", self.switchMainWindow)
		
	def onDragMotion(self, widget, context, x, y, time):
		context.drag_status(gtk.gdk.ACTION_COPY, time)
		if not self.dragging:
			self.switchMainWindow()
		self.dragging = True
		return True

	def onDragLeave(self, widget, context, time):
		self.dragging = False
		
	def switchMainWindow(self):
		visible = not self.mainWindow.get_property('visible')
		
		self.mainWindow.set_property('visible', visible)
		
		if visible:
			x,y,w,h = self.allocation
			x,y = self.window.get_origin()
			self.mainWindow.move(x, y+h)
			self.mainWindow.grabFocus()
			
		self.set_active(not visible)
	
	def onFocusChange(self, widget, event):
		print "Focus has changed!"
		if gtk.gdk.window_at_pointer() == None:
			self.set_active(False)
			self.mainWindow.hide()
			
	def onButtonPressEvent(self, widget, event):
		if event.button == 1:
			self.switchMainWindow()
		elif event.button == 3:
			self.emit_stop_by_name("button_press_event")
			
	def onAboutClicked(self, *arguments, **keywords):
		about = gnome.ui.About(
			"USP2",
			"v1.0 preview",
			"Copyright 2007 Tomaž Vajngerl - Based on the original idea by S.Chanderbally.",
			"Modular system launcher for GNOME desktop",
			["Developers:",
				"Tomaž Vajngerl <quikee@gmail.com>"],
				[])
		about.show()

	def onReloadClicked(self, *arguments, **keywords):
		# Reload is broken!
		while gtk.events_pending():
			gtk.main_iteration()
		self.mainWindow.destroy()
		self.mainWindow = MainWindow()
		self.mainWindow.add_events(gtk.gdk.FOCUS_CHANGE)
		self.mainWindow.connect("focus_out_event", self.onFocusOut)

	def onPreferencesClicked(self, *arguments, **keywords):
		pass

			
class MainApplet(object):
	@staticmethod
	def factory(applet, iid):
		MainApplet(applet)
		return True
	
	def __init__(self, applet):
		appletWidget = AppletWidget(applet)
		applet.add(appletWidget)
		applet.connect("change_background", self.onChangeBackground)
		
		propxml="""
				<popup name="button3">
					<menuitem name="Item 1" verb="About" label="_About" pixtype="stock" pixname="gtk-about"/>
					<menuitem name="Item 2" verb="Reload" label="_Reload" pixtype="stock" pixname="gtk-refresh"/>
					<menuitem name="Item 2" verb="Preferences" label="_Preferences" pixtype="stock" pixname="gtk-properties"/>
				</popup>"""
		verbs = [
			("About", appletWidget.onAboutClicked), 
			("Reload", appletWidget.onReloadClicked),
			("Preferences", appletWidget.onPreferencesClicked)]
		applet.setup_menu(propxml, verbs, None)
		
		applet.show_all()
		
	def onChangeBackground(self, applet, type, color, pixmap):
		applet.set_style(None)
		rc_style = gtk.RcStyle()
		applet.modify_style(rc_style)
		if (type == gnomeapplet.COLOR_BACKGROUND):
			applet.modify_bg(gtk.STATE_NORMAL, color)
		elif (type == gnomeapplet.PIXMAP_BACKGROUND):
			style = applet.style
			style.bg_pixmap[gtk.STATE_NORMAL] = pixmap
			self.applet.set_style(style)
			

if __name__ == '__main__':
	if len(sys.argv) == 2 and sys.argv[1] == "run-in-window":
		mainWindow = gtk.Window(gtk.WINDOW_TOPLEVEL)
		mainWindow.set_title("Ubuntu System Panel 2.0")
		mainWindow.connect("destroy", gtk.main_quit)
		gnome.init("Ubuntu System Panel 2.0", "1.0")
		applet = gnomeapplet.Applet()
		MainApplet.factory(applet, None)
		applet.reparent(mainWindow)
		mainWindow.show_all()
		gtk.main()
	else:
		gnomeapplet.bonobo_factory("OAFIID:Ubuntu_System_Panel_Factory", gnomeapplet.Applet.__gtype__, "Ubuntu System Panel 2.0", "1.0", MainApplet.factory)
